﻿using System;
using System.Linq;
using System.Web.Mvc;
using Common.Extensions;
using ContactManagement.AppCode;
using ContactManagement.AppCode.CategoryManagement;
using ContactManagement.AppCode.MailManagement;
using ContactManagement.Models;
using EFDataModel;
using EFDataModel.Enums;

namespace ContactManagement.Controllers
{
    #region Public Views
    [CustomAuthorize]
    public class UserController : Controller
    {
        /// <summary>
        /// Anzeigen der UserListe
        /// GET: /User/UserList
        /// </summary>
        public ActionResult UserList()
        {
            UserListModel userListModel = new UserListModel();
            //Ausstellen das beim Laden der Liste das Tracking eingeschalten ist, was den Ladevorgang erheblich schneller macht.
            //http://blogs.msdn.com/b/adonet/archive/2011/02/05/using-dbcontext-in-ef-feature-ctp5-part-11-load-and-asnotracking.aspx
           
            //Die Daten werden hier noch nicht sortiert oder per Paging angepasst, da wir die originalanzahl an Elemente für das Paging
            //benötigen, wird werden die Paging und Order elemente erst in der ForEach Schleife auf der Webseite organisiert.
            userListModel.Users = (from eUser in CurrentHttpContext.GetDataModel().EFBaseMenge.AsNoTracking().OfType<EFUser>()
                                   select eUser);
            
            return View(userListModel);
        }

        /// <summary>
        /// Anzeigen der Benutzerdetails, von der übergebenen Id
        /// </summary>
        /// <param name="id">User Id die angezeigt werden soll</param>
        public ActionResult UserDetails(int id)
        {
            //Den user mit der übergebenen User id laden
            EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
            if (efUser != null)
            {
                DetailsUserAdminModel detailsUserAdminModel = new DetailsUserAdminModel(efUser);
                detailsUserAdminModel.UserRights = efUser.GetUserRights();
                detailsUserAdminModel.UserGroups = efUser.UserGroups;
                return View(detailsUserAdminModel);
            }

            //Auf die Startseite verweisen
            return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
        }

        /// <summary>
        /// Anzeigen der Benutzerdetails, von der übergebenen Id
        /// </summary>
        /// <param name="id">User Id die angezeigt werden soll</param>
        public ActionResult UserEdit(int id)
        {
            //Den user mit der übergebenen User id laden
            EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
            if (efUser != null)
            {
                return View(new EditUserAdminModel(efUser));
            }

            //Auf die Startseite verweisen
            return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
        }

        /// <summary>
        /// Anzeigen der Benutzerdetails, von der übergebenen Id
        /// </summary>
        /// <param name="model">Daten aus dem Model vom Post</param>
        /// <param name="id">die aktuelle UserId die bearbeitet wird.</param>
        [HttpPost]
        public ActionResult UserEdit(int id, EditUserAdminModel model)
        {
            //Bei einem Postback muss die Id wieder gesetzt werden, da diese nicht automatisch im Model landet!
            model.UserId = id;

            if (ModelState.IsValid)
            {
                //Den user mit der übergebenen User id laden
                EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
                if (efUser != null)
                {
                    //Prüfen ob sich der Username geändert hat und wenn ja prüfen ob dieser Bereits vergeben wurde.
                    if (efUser.LoginEmail.ToLower() != model.UserName.ToLower())
                    {
                        //Prüfen ob der Username schon existiert
                        if (EFUser.CheckUserExists(model.UserName, CurrentHttpContext.GetDataModel()))
                        {
                            CustomMessage.CurrentMessage(this).AddMessage(App_GlobalResources.SystemMessages.UserExistsErrorRESX, MessageTypes.error);
                            return View(model);
                        }
                    }

                    //Setzen der Daten aus dem Model im übergebenen EFUser.
                    SetUserDataFromModel(efUser, model);
                    CustomMessage.CurrentMessage(this).AddMessage(App_GlobalResources.SystemMessages.SaveSuccessfullRESX, MessageTypes.ok);
                }
                else
                {
                    //Auf die Startseite verweisen
                    return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
                }
            }

            return View(model);
        }

        /// <summary>
        /// Anlegen eines neuen Users
        /// GET: /User/UserAdd
        /// </summary>
        /// <returns></returns>
        public ActionResult UserAdd()
        {
            UserAddModel userAddModel = new UserAddModel();
            return View(userAddModel);
        }

        /// <summary>
        /// Anlegen eines neuen Users. (Im Model ist kein PW enthalten, dieses kann nur direkt per Mail zugesendet werden.
        /// POST: /User/UserAdd
        /// </summary>
        [HttpPost]
        public ActionResult UserAdd(UserAddModel model)
        {
            //Prüfen wo das ebenfalls noch verwendet wird!
            if (ModelState.IsValid)
            {
                //Prüfen ob ein User mit dem gleichen Namen bereits existiert und nur wenn nicht kann der User auch angelegt werden.
                var userExists = (from eUser in CurrentHttpContext.GetDataModel().EFBaseMenge.OfType<EFUser>() where eUser.LoginEmail == model.UserName select eUser.ID).Count();
                if (userExists > 0)
                {
                    CustomMessage.CurrentMessage(this).AddMessage(App_GlobalResources.SystemMessages.UserExistsErrorRESX,MessageTypes.error);
                }
                else
                {
                    //Erstellen eines neuen Users und dem Model hinzufügen und speichern
                    EFUser efUser = new EFUser();
                    SetUserDataFromModel(efUser, model);
                    //Per Attribut wird bereits geprüft, das die Mailadresse nicht doppelt angegeben werden darf / kann
                    efUser.LoginEmail = model.UserName;
                    efUser.CreationDate = DateTime.Now;
                    efUser.EditDate = DateTime.Now;
                    efUser.EditorID = CurrentSession.GetCurrentUser().ID;
                    //Als ersteller den aktuellen User eintragen
                    efUser.CreatorID = CurrentSession.GetCurrentUser().ID;
                    //WICHTIG die ModulId muss gesetzt werden
                    efUser.EFModule = EFModules.Member;

                    //TODO ENTFERNEN DAS DAS PW AUTOMATISCH GESETZT WIRD, sonst nur per Mail möglich!!
                    if (System.Web.HttpContext.Current.Server.MachineName.ToLower().Contains("squad"))
                    {
                        efUser.SetPassword(efUser.Firstname);
                    }
                    else
                    {
                        //Einfach ein zufälliges PW setzen, was keiner kennt, das richtige PW kann man nur per Mail an den User versenden.
                        string randomString = Common.Password.PasswordHash.GetRandomString(40, true);
                        efUser.SetPassword(Common.Password.PasswordHash.GetPasswordHash(randomString, DateTime.Now.ToShortTimeString(), 20));
                    }

                    CurrentHttpContext.GetDataModel().EFBaseMenge.Add(efUser);
                    CurrentHttpContext.GetDataModel().SaveChanges();
                    //Ausgabe das der User erfolgreich angelegt wurde.
                    CustomMessage.CurrentMessage(this).AddMessage(App_GlobalResources.SystemMessages.UserCreatedSuccesfullyRESX, MessageTypes.ok);

                    //Mit der neu erstellen ID des Users aus dem Model, auf die Bearbeiten Ansicht des Users umstellen.
                    return RedirectToAction(EActionUser.UserEdit.ToString(), new { id = efUser.ID });
                }
            }

            return View(model);
        }

        /// <summary>
        /// Für die übergenene UserId ein neues Passwort erstellen und dem User zusenden, diese Funktion kann nur vom Administrator 
        /// ausgelöst werden.
        /// </summary>
        /// <param name="id">UserId für die ein Neues PW gesetzt werden soll</param>
        public ActionResult UserSendNewPassword(int id)
        {
            //Erstellen der Mail und setzen des neuen Passwortes
            AddUserMail addUserMail = new AddUserMail(this);
            if (addUserMail.SendUserPasswordMail(id))
            {
                int userId = id;
                return RedirectToAction(EActionUser.UserDetails.ToString(), new { id = userId });
            }

            //Auf die Userliste verweisen, wenn ein Fehler aufgetreten ist.
            return RedirectToAction(EActionUser.UserList.ToString());
        }

        /// <summary>
        /// Anzeigen des übergeben Users
        /// GET: /User/Details/5
        /// </summary>
        /// <param name="id">Die Id des Users der angezeigt werden soll.</param>
        [CustomAuthorize]
        public ActionResult Details(int id)
        {
            EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
            if (efUser != null)
            {
                //Für Details und Edit wird das gleiche Model verwendet.
                return View(new EditUserModel(efUser));
            }

            //Auf die Startseite verweisen
            return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
        }

        /// <summary>
        /// Anzeigen der Bearbeitung für den User
        /// GET: /User/Edit/5 
        /// </summary>
        /// <param name="id">Die UserId die bearbeitet werden soll</param>
        [CustomAuthorize]
        public ActionResult Edit(int id)
        {
            //Den User mit der übergebenen UserId laden
            EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
            if (efUser != null)
            {
                return View(new EditUserModel(efUser));
            }

            //Auf die Startseite verweisen
            return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
        }

        /// <summary>
        /// Bearbeitung des Users speichern und die übergebenen Modeldaten im efUser speichern
        /// POST: /User/Edit/5
        /// </summary>
        [HttpPost]
        [CustomAuthorize]
        public ActionResult Edit(int id, EditUserModel editUserModel)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    //Laden des passenden EF Users aus der DB
                    EFUser efUser = (EFUser)CurrentHttpContext.GetDataModel().EFBaseMenge.Find(id);
                    if (efUser != null)
                    {
                        efUser.Firstname = editUserModel.Firstname;
                        efUser.Lastname = editUserModel.Lastname;
                        efUser.Telephone = editUserModel.Handy ?? string.Empty;
                        efUser.Birthdate = editUserModel.Birthdate;
                        efUser.UserTitleID = int.Parse(editUserModel.UserTitle);
                        //Speichern der Änderungen
                        CurrentHttpContext.GetDataModel().SaveChanges();

                        ModelState.AddModelError("", App_GlobalResources.SystemMessages.SaveSuccessfullRESX);
                        //Den MessageType im ViewBag ablegen, hier wird beim Div dann einfach eine Css Class gesetzt mit diesem Wert
                        ViewBag.MessageType = MessageTypes.ok.ToString();

                        //Für beim erfolgreichen Speichern wieder das Model anzeigen
                        return View(editUserModel);
                    }
                    else
                    {
                        //Auf die Startseite verweisen
                        return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
                    }
                }
                catch
                {
                    //Fehlermeldung erstellen
                    CustomMessage.CurrentMessage(this).AddMessage(App_GlobalResources.SystemMessages.ErrorPleaseTryAgainLaterRESX, MessageTypes.error);
                    //Auf die Startseite verweisen bei einem Fehler
                    return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
                }
            }

            return View(editUserModel);
        }

        /// <summary>
        /// View anzeigen in dem das Passwort geändert werden kann
        /// GET: /User/ChangePassword
        /// </summary>
        [Authorize]
        public ActionResult ChangePassword()
        {
            return View();
        }

        /// <summary>
        /// Änderung des Passwortes veranlassen, jeder User darf nur sein eigenes PW ändern, alles andere ist nicht zulässig
        /// daher wird hier in der URL auch keine UserId übergeben!
        /// POST: /User/ChangePassword 
        /// </summary>
        [Authorize]
        [HttpPost]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            if (ModelState.IsValid)
            {
                //Prüfen ob das alte Passwort übereinstimmt.
                if (CurrentSession.GetCurrentUser().CheckPassword(model.OldPassword))
                {
                    //Setzten des neuen Passworts und Speichern des "geänderten" Users
                    CurrentSession.GetCurrentUser().SetPassword(model.NewPassword);
                    CurrentHttpContext.GetDataModel().SaveChanges();

                    return RedirectToAction(EActionUser.ChangePasswordSuccess.ToString(), EControllers.User.ToString());
                }
                else
                {
                    ModelState.AddModelError("", App_GlobalResources.SystemMessages.OldPasswordWrongRESX);
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        /// <summary>
        /// Die Bestätigungsseite für das erfolgreiche Ändern des Passwort anzeigen
        /// GET: /User/ChangePasswordSuccess
        /// </summary>
        [Authorize]
        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }

        /// <summary>
        /// Anzeigen in welchen Gruppen der User mitlgied ist und in welchen nicht.
        /// </summary>
        /// <param name="id">UserId für die angezeigt werden soll in welchen Gruppen der User Mitglied ist.</param>
        public ActionResult UserEditUserGroups(int id)
        {
            //Das ViewModel mit den passenden Daten laden und zurückgeben
            UserGroupsListModel userGroupsListModel = WebUserGroups.UserGroups(this).UserGroupsByUser(id);
            return View(userGroupsListModel);
        }

        /// <summary>
        /// Die Gruppe zum User hinzufügen.
        /// </summary>
        /// <param name="id">Gruppe die dem User hinzugefügt werden soll</param>
        /// <param name="userId">User der der Gruppe hinzugefügt werden soll</param>
        public ActionResult UserEditUserGroupsAdd(int id, int userId)
        {
            WebUserGroups.UserGroups(this).AddUserToGroup(id, userId);
            //Wieder auf die Seite verweisen, wo die UserRights des Users aufgelistet werden
            return RedirectToAction(EActionUser.UserEditUserGroups.ToString(), new { id = userId });
        }

        /// <summary>
        /// Die Gruppe die vom User entfernt werden soll
        /// </summary>
        /// <param name="id">Die Gruppe die vom User entfernt werden soll</param>
        /// <param name="userId">Der User von dem die Gruppe entfernt werden soll</param>
        public ActionResult UserEditUserGroupsDelete(int id, int userId)
        {
            WebUserGroups.UserGroups(this).DeleteUserFromGroup(id, userId);
            //Wieder auf die Seite verweisen, wo die UserRights des Users aufgelistet werden
            return RedirectToAction(EActionUser.UserEditUserGroups.ToString(), new { id = userId });
        }

        /// <summary>
        /// Anzeige aller Rechte die dem User zugewiesen sind bzw. die ihm nicht zugewiesen sind.
        /// Inklusive der der Überprüfung der Gruppen in denen der User Mitglied ist.
        /// </summary>
        /// <param name="id">UserId für den alle Rechte angezeigt werden sollen</param>
        public ActionResult UserEditUserRights(int id)
        {
            UserRightListModel userRightListModel = WebUserRights.UserRights(this).UserRightsByUserId(id);
            if (userRightListModel != null)
            {
                return View(userRightListModel);
            }

            //Auf die Startseite verweisen, wenn nach einer Kategorie gefragt wird die nicht existiert.
            return RedirectToAction(EActionHome.Index.ToString(), EControllers.Home.ToString());
        }

        /// <summary>
        /// Hinzufügen von UserRights zum User
        /// </summary>
        /// <param name="id">UserId zu der das Recht hinzugefügt werden soll</param>
        /// <param name="rightId">UserRight das dem user hinzugefügt werden soll</param>
        public ActionResult UserEditUserRightsAdd(int id, int rightId)
        {
            WebUserRights.UserRights(this).AddUserRightToUser(id, rightId);
            string userId = id.ToString();
            //Wieder auf die Seite verweisen, wo die UserRights des Users aufgelistet werden
            return RedirectToAction(EActionUser.UserEditUserRights.ToString(), new { id = userId });
        }

        /// <summary>
        /// Entfernen von UserRights vom User
        /// </summary>
        /// <param name="id">UserId von der das UserRight entfernt werden soll</param>
        /// <param name="rightId">UserRightId das vom User entfernt werden soll</param>
        public ActionResult UserEditUserRightsDelete(int id, int rightId)
        {
            WebUserRights.UserRights(this).DeleteUserRightFromUser(id, rightId);
            string userId = id.ToString();
            //Wieder auf die Seite verweisen, wo die UserRights des Users aufgelistet werden
            return RedirectToAction(EActionUser.UserEditUserRights.ToString(), new { id = userId });
        }

        /// <summary>
        /// Entfernen der UserRights vom User auch die Gruppen entfernen von denen er das Recht erbt.
        /// </summary>
        /// <param name="id">RightId die entfernt werden soll vom User.</param>
        /// <param name="userId">UserId bei der das Recht entfernt werden soll</param>
        public ActionResult UserEditUserRightsDeleteComplete(int id, int userId)
        {
            WebUserRights.UserRights(this).DeleteGroupRightsFromUser(userId, id);
            //Wieder auf die Seite verweisen, wo die UserRights des Users aufgelistet werden
            return RedirectToAction(EActionUser.UserEditUserRights.ToString(), new { id = userId });
        }
    #endregion

        #region Private Functions
        /// <summary>
        /// Die Userdaten aus dem Model im EFUser hinterlegen.
        /// </summary>
        /// <param name="efUser">der EF User bei dem die Daten aktualisiert werden sollen</param>
        /// <param name="model">Das Model aus dem die Daten geladen werden sollen</param>
        private void SetUserDataFromModel(EFUser efUser, EditUserAdminModel model)
        {
            //die Nutzerdaten von der webseite mit denen im Model aktualisieren
            efUser.Firstname = model.Firstname;
            efUser.Lastname = model.Lastname;
            efUser.LoginEmail = model.UserName ?? string.Empty;
            efUser.Telephone = model.Handy ?? string.Empty;
            efUser.IsActiveUser = model.IsActiveUser;
            efUser.Logintries = model.Logintries;
            //Setzten der Adresse aus dem Model
            efUser.SetDefaultAddress(model.Street, model.HouseNumber, model.City, model.ZipCode, null, CurrentSession.GetCurrentUser().ID);
            efUser.Birthdate = model.Birthdate;
            efUser.UserCategoryID = int.Parse(model.Category);
            efUser.UserTitleID = int.Parse(model.UserTitle);

            //Der UserType kann nur von einem Admin oder SuperUser bearbeitet werde, daher bleibt der alte Wert erhalten sollte es sich um einen normalen User handeln.
            if (CurrentSession.GetCurrentUser().IsAdminOrSuperUser())
            {
                efUser.UserType = (EUserTypes)Enum.Parse(typeof(EUserTypes), model.UserType);
            }
            //Wenn der Benutzer neu angelegt wird und es sich nicht um einen Admin handelt wird automatisch Member als BenutzerTyp festgelegt.
            else if (efUser.ID == 0)
            {
                efUser.UserType = EUserTypes.Member;
            }

            CurrentHttpContext.GetDataModel().SaveChanges();
        }
        #endregion
    }
}
